/************************************************************
* @file  gkt_driver.h
************************************************************/

#ifndef GKT_DRIVER_H
#define GKT_DRIVER_H

#include "gkt_types.h"
#include "gkt_defines.h"

#ifdef __cplusplus
extern "C" {
#endif

/* 
 * Driver ID
 */
enum {
	GKT_DRVID_NONE = 0,

	GKT_DRVID_GPIO,
	GKT_DRVID_UART,
	GKT_DRVID_I2C,
	GKT_DRVID_I2S,
	GKT_DRVID_SSI,
	GKT_DRVID_DMA,
	GKT_DRVID_TIMER,
	GKT_DRVID_EFLASH,
	GKT_DRVID_CMOS,
	GKT_DRVID_VPWM,
	GKT_DRVID_RTC,
	GKT_DRVID_RNG,
	GKT_DRVID_WDT,
	GKT_DRVID_CRYPTO,
	GKT_DRVID_TOUCHKEY,
	GKT_DRVID_USB,

	GKT_DRVID_MAX
};

/* lp mode */
typedef enum {
	GKT_DRIVER_LP_NORMAL = 0,
	GKT_DRIVER_LP_FAST_RESTORE,
	GKT_DRIVER_LP_MAX
} gkt_driver_lp_mode_e;

#define GKT_DEVICE_ITEM(name, id, item)	GKT_##name##id##_##item

/*
 * Base Attr
 */
typedef struct gkt_device_base_attr {
	uint32_t	base_addr;
	uint16_t	irq_no;
	uint16_t	irq_priority;
} gkt_device_base_attr_s;
#define GKT_DEVICE_BASE_ATTR(DEV_NAME, DEV_ID)	\
	{	\
		.base_addr = GKT_##DEV_NAME##DEV_ID##_##BASEADDR,	\
		.irq_no = GKT_##DEV_NAME##DEV_ID##_##IRQ_NO,	\
		.irq_priority = GKT_IRQ_PRIORITY(GKT_##DEV_NAME##DEV_ID##_##IRQ_PRIORITY),\
	}


typedef int (*gkt_device_init_fn)(void);

/*
 * device descriptor
 */
typedef struct _gkt_device_desc	gkt_device_desc_s;
struct _gkt_device_desc {
#if defined(GKT_CONFIG_DEBUG_ENABLE) && GKT_CONFIG_DEBUG_ENABLE
	const char	*name_str;
#endif
	const gkt_device_desc_s *const *depend_devs;
	gkt_device_init_fn	init;
};
#if defined(GKT_CONFIG_DEBUG_ENABLE) && GKT_CONFIG_DEBUG_ENABLE
#define GKT_EVICE_DESC_NAME_STR_DEFINE(name)	\
	.name_str	= #name,
#else
#define GKT_EVICE_DESC_NAME_STR_DEFINE(name)
#endif


#if defined(__GNUC__)
#define __DEVINFO_DESC_BASE	__SECTION(.rodata.devinfo.desc0)	/* base */
#define __DEVINFO_DESC_NORMAL	__SECTION(.rodata.devinfo.desc1)	/* normal */
#define __DEVINFO_DESC_EXTERNAL	__SECTION(.rodata.devinfo.desc2)	/* external */
#define __DEVINFO_DESC(level)	__DEVINFO_DESC_##level

#define __DEVINFO_DEPTBL	__SECTION(.rodata.devinfo.deptbl)
#define __DEVINFO_ATTR	__SECTION(.rodata.devinfo.attr)
#define __DEVINFO_RODATA	__SECTION(.rodata.devinfo)
#define __DEVINFO_DATA	__SECTION(.data.devinfo)
#define __DEVINFO_BSS	__SECTION(.bss.devinfo)

#define GKT_DEVICE_DESC(name)	__gc_device_##name##_desc
#define GKT_DEVICE_DESC_DECLARE(name)	\
	const gkt_device_desc_s GKT_DEVICE_DESC(name)
#define GKT_DEVICE_DEPTBL(name)	__gc_device_##name##_devs_deptbl

#define GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, devs_deptbl)	\
	GKT_DEVICE_DESC_DECLARE(name) __DEVINFO_DESC(level) __USED =	\
	{	\
		GKT_EVICE_DESC_NAME_STR_DEFINE(name)	\
		.depend_devs	= devs_deptbl,	\
		.init = gkt_##name##_init,	\
	}

#define GKT_DEVICE_DESC_DEFINE(name, level)	\
	GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, NULL)

/* device with 1 dependent device */
#define GKT_DEVICE_DESC_DEFINE_1(name, level, dep_dev0)	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev0);	\
	const gkt_device_desc_s *const GKT_DEVICE_DEPTBL(name)[] __DEVINFO_DEPTBL =	\
	{	\
		&GKT_DEVICE_DESC(dep_dev0),	\
		NULL,	\
	};	\
	GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, GKT_DEVICE_DEPTBL(name))

/* device with 2 dependent devices */
#define GKT_DEVICE_DESC_DEFINE_2(name, level, dep_dev0, dep_dev1)	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev0);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev1);	\
	const gkt_device_desc_s *const GKT_DEVICE_DEPTBL(name)[] __DEVINFO_DEPTBL =	\
	{	\
		&GKT_DEVICE_DESC(dep_dev0),	\
		&GKT_DEVICE_DESC(dep_dev1),	\
		NULL,	\
	};	\
	GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, GKT_DEVICE_DEPTBL(name))

/* device with 3 dependent devices */
#define GKT_DEVICE_DESC_DEFINE_3(name, level, dep_dev0, dep_dev1, dep_dev2)	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev0);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev1);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev2);	\
	const gkt_device_desc_s *const GKT_DEVICE_DEPTBL(name)[] __DEVINFO_DEPTBL =	\
	{	\
		&GKT_DEVICE_DESC(dep_dev0),	\
		&GKT_DEVICE_DESC(dep_dev1),	\
		&GKT_DEVICE_DESC(dep_dev2),	\
		NULL,	\
	};	\
	GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, GKT_DEVICE_DEPTBL(name))

/* device with 4 dependent devices */
#define GKT_DEVICE_DESC_DEFINE_4(name, level, dep_dev0, dep_dev1, dep_dev2, dep_dev3)	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev0);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev1);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev2);	\
	extern GKT_DEVICE_DESC_DECLARE(dep_dev3);	\
	const gkt_device_desc_s *const GKT_DEVICE_DEPTBL(name)[] __DEVINFO_DEPTBL =	\
	{	\
		&GKT_DEVICE_DESC(dep_dev0),	\
		&GKT_DEVICE_DESC(dep_dev1),	\
		&GKT_DEVICE_DESC(dep_dev2),	\
		&GKT_DEVICE_DESC(dep_dev3),	\
		NULL,	\
	};	\
	GKT_DEVICE_DESC_DEFINE_WITH_DEPTBL(name, level, GKT_DEVICE_DEPTBL(name))
#else
#define __DEVINFO_DESC
#define __DEVINFO_DEPTBL
#define __DEVINFO_ATTR
#define __DEVINFO_RODATA
#define __DEVINFO_DATA
#define __DEVINFO_BSS

#define GKT_DEVICE_DESC_DEFINE(name, level)
#define GKT_DEVICE_DESC_DEFINE_1(name, level, dep_dev0)
#define GKT_DEVICE_DESC_DEFINE_2(name, level, dep_dev0, dep_dev1)
#define GKT_DEVICE_DESC_DEFINE_3(name, level, dep_dev0, dep_dev1, dep_dev2)
#define GKT_DEVICE_DESC_DEFINE_4(name, level, dep_dev0, dep_dev1, dep_dev2, dep_dev3)
#endif

/*
 * device low_power hook
 */
typedef void (*gkt_device_lp_hook_fn)(int when, int mode);
#define __DEVICE_LP_HOOK	__SECTION(.rodata.hook.device_lp)
#define GKT_DEVICE_LP_HOOK_DEFINE(hook_fn)	\
	const gkt_device_lp_hook_fn __##hook_fn \
		__DEVICE_LP_HOOK __USED = hook_fn

typedef void (*gkt_device_rtc_alarm_fn)(void);
void gkt_driver_register_rtc_alarm(gkt_device_rtc_alarm_fn rtc_alarm);

/*
 * devices attrs define
 *
 * params:
 *   DEV_NAME : uppercase device name, GPIO, ...
 *   dev_name : lowercase device name, gpio, ...
 *
 * e.g:
 *   GKT_DEVICE_ATTRS_DEFINE(GPIO, gpio)
 *   before referring this macro, you should
 *   1. typedef struct { ... } ${dev_name}_attr_s
 *   2. #define GPIO_ATTR_DEFINE(dev_id)	{	... }
 */
#define GKT_DEVICE_ATTRS_DEFINE(DEV_NAME, dev_name)	\
	const dev_name##_attr_s __gc_##dev_name##_attrs[DEV_NAME##_DEVNUMS] __DEVINFO_ATTR = {	\
		GKT_MULTI_ITEMS_DEFINE(DEV_NAME##_ATTR, DEV_NAME##_DEVNUMS)	\
	}
#define GKT_DEVICE_ATTRS_DECLARE(dev_name)	\
	extern const dev_name##_attr_s __gc_##dev_name##_attrs[]
#define GKT_DEVICE_ATTR(dev_name, dev_id)	\
	__gc_##dev_name##_attrs[dev_id]


/*
 * device instances define
 *
 * params:
 *   DEV_NAME : uppercase device name, GPIO, ...
 *   dev_name : lowercase device name, gpio, ...
 *
 * e.g:
 *   GKT_DEVICE_INSTANCES_DEFINE(GPIO, gpio)
 *   before referring this macro, you should
 *   1. typedef struct { ... } gpio_instance_s
 *   2. #define GPIO_INSTANCE_DEFINE(dev_id)	{	... }
 */
#define GKT_DEVICE_INSTANCES_DEFINE(DEV_NAME, dev_name)	\
	dev_name##_instance_s __g_##dev_name##_instances[DEV_NAME##_DEVNUMS] = {	\
		GKT_MULTI_ITEMS_DEFINE(DEV_NAME##_INSTANCE, DEV_NAME##_DEVNUMS)	\
	}
#define GKT_DEVICE_INSTANCE_DECLARE(dev_name)	\
	extern dev_name##_instance_s __g_##dev_name##_instances[]
#define GKT_DEVICE_INSTANCE(dev_name, dev_id)	\
	__g_##dev_name##_instances[dev_id]


/* calculate the total item nums of CLASS_NAME
 * e.g:
 *   SPI0 has 4 slaves
 *   SPI1 has 3 slaves
 *  GKT_DEVICE_ITEM_NUMS(SPI_SLAVE, GKT_SPI_DEVNUMS) ==> result is 7, a constant value during compiling stage
 *  before using this macro, you should define a macro as below
 *   #define SPI_SLAVE_NUMS(dev_id)	GKT_SPI##dev_id##_SLAVE_NUMS
 * or
 *   #define SPI_SLAVE_NUMS(dev_id)	GKT_SPI_SLAVE_NUMS
 */
#define GKT_DEVICE_ITEM_NUMS_1(CLASS_NAME)	\
	CLASS_NAME##_NUMS(0)
#define GKT_DEVICE_ITEM_NUMS_2(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_1(CLASS_NAME) + CLASS_NAME##_NUMS(1))
#define GKT_DEVICE_ITEM_NUMS_3(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_2(CLASS_NAME) + CLASS_NAME##_NUMS(2))
#define GKT_DEVICE_ITEM_NUMS_4(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_3(CLASS_NAME) + CLASS_NAME##_NUMS(3))
#define GKT_DEVICE_ITEM_NUMS_5(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_4(CLASS_NAME) + CLASS_NAME##_NUMS(4))
#define GKT_DEVICE_ITEM_NUMS_6(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_5(CLASS_NAME) + CLASS_NAME##_NUMS(5))
#define GKT_DEVICE_ITEM_NUMS_7(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_6(CLASS_NAME) + CLASS_NAME##_NUMS(6))
#define GKT_DEVICE_ITEM_NUMS_8(CLASS_NAME)	\
	(GKT_DEVICE_ITEM_NUMS_7(CLASS_NAME) + CLASS_NAME##_NUMS(7))

#define GKT_DEVICE_ITEM_NUMS_L2(CLASS_NAME, nums)	\
	GKT_DEVICE_ITEM_NUMS_##nums(CLASS_NAME)
#define GKT_DEVICE_ITEM_NUMS_L1(CLASS_NAME, nums)	\
	GKT_DEVICE_ITEM_NUMS_L2(CLASS_NAME, nums)
#define GKT_DEVICE_ITEM_NUMS(CLASS_NAME, nums)	GKT_DEVICE_ITEM_NUMS_L1(CLASS_NAME, nums)


int gkt_driver_init(void);

void gkt_driver_set_lp_mode(gkt_driver_lp_mode_e lp_mode);
gkt_driver_lp_mode_e gkt_driver_get_lp_mode(void);

void gkt_driver_clock_aon_32k_update(void);

void gkt_driver_device_power_up(void);
void gkt_driver_device_power_down(void);

int gkt_driver_clock_aon_32k_measure_is_start(void);
int gkt_driver_clock_aon_32k_measure_control(int start, uint32_t interval_us);
uint32_t gkt_driver_clock_aon_32k_measure_result(void);

#ifdef __cplusplus
}
#endif

#endif

